#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <thread>
#include <unistd.h>
#include "rtdev/tio/tioV3/tiov3.h"
#include "rtbus/rtbus.h"
#include <cstring>

using ::testing::_;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::SetArrayArgument;

template <typename T> void adapt_void_ptr(void* ptr, T value)
{
    printf("\n[adapt_void_ptr] exec!\n");
    printf("[adapt_void_ptr] size:%ld \n", sizeof(T));
    // 打印 value 的值
    if constexpr (std::is_floating_point<T>::value)
    {
        printf("[adapt_void_ptr] value: %f\n", value);  // 对于浮点类型
    }
    else if constexpr (std::is_same<T, std::string>::value)
    {
        printf("[adapt_void_ptr] value: %s\n", value.c_str());  // 对于 std::string 类型
    }
    else if constexpr (std::is_same<T, uint32_t>::value)
    {
        printf("[adapt_void_ptr] uint32_t value: %u\n", value);  // 对于 uint_8 类型
    }
    else if constexpr (std::is_same<T, uint64_t>::value)
    {
        printf("[adapt_void_ptr] uint64_t value: %lu\n", value);  // 对于 uint_8 类型
    }
    else if constexpr (std::is_integral<T>::value)
    {
        printf("[adapt_void_ptr] value: %d\n", value);  // 对于整数类型
    }
    else
    {
        printf("[adapt_void_ptr] value: (unsupported type)\n");  // 不支持的类型
    }
    if (ptr)
    {
        memcpy(ptr, &value, sizeof(T));
    }
}

class MockRtCommuItf : public rtbus::RtCommuItf
{
public:
    // MOCK_METHOD 用于模拟纯虚函数
    MOCK_METHOD(int, open, (RtCommuItf::BusCfg cfg), (override));
    MOCK_METHOD(void, close, (), (override));
    MOCK_METHOD(int, active, (), (override));
    MOCK_METHOD(int, deactive, (), (override));
    MOCK_METHOD(int,
                register_sdo_handle,
                (uint8_t slave_id, uint32_t write_req_frame_id, uint32_t read_req_frame_id, uint32_t resp_frame_id, size_t obj_id_size),
                (override));
    MOCK_METHOD(void, send, (), (override));
    MOCK_METHOD(void, recv, (int64_t * timeoff), (override));
    MOCK_METHOD(void, reset, (), (override));
    MOCK_METHOD(bool, is_actived, (), (override));
    MOCK_METHOD(int, get_sdo, (uint8_t slave_id, uint32_t obj_id, std::function<int(uint8_t*, int)> cb, int timeout, int retry), (override));
    MOCK_METHOD(int,
                set_sdo,
                (uint8_t slave_id, uint32_t obj_id, uint8_t val[4], size_t size, std::function<int(uint8_t*, int)> cb, int timeout, int retry),
                (override));
    MOCK_METHOD(int, get_sdo_blocking, (uint8_t slave_id, uint32_t objid, void* data, int s, int retry), (override));
    MOCK_METHOD(int, set_sdo_blocking, (uint8_t slave_id, uint32_t objid, void* data, int s, void* respdata, int retry), (override));
    MOCK_METHOD(int, config_txpdo, (uint32_t obj, size_t size), (override));
    MOCK_METHOD(int, config_rxpdo, (uint32_t obj, size_t size), (override));
    MOCK_METHOD(int, set_txpdo_data, (uint32_t obj, uint8_t* data), (override));
    MOCK_METHOD(int, get_rxpdo_data, (uint32_t obj, uint8_t* data), (override));
};

// 测试类初始化
class TioV3BoardTest : public ::testing::Test
{
protected:
    std::shared_ptr<TioV3Board> board;
    std::shared_ptr<MockRtCommuItf> mockBus;
    void SetUp() override
    {
        printf("\n[TioV3BoardTest] SetUp\n");
        board = std::make_shared<TioV3Board>();
        mockBus = std::make_shared<MockRtCommuItf>();
        board->bus_ = mockBus;
    }
    void TearDown() override
    {
        printf("\n[TioV3BoardTest] TearDown\n");
        mockBus.reset();
        board.reset();
    }
};

// 测试query_Tio_version
TEST_F(TioV3BoardTest, QueryTioVersion)
{
    EXPECT_CALL(*mockBus, get_sdo_blocking(_, TIO_VERSION_R, _, _, _))
        .WillOnce(DoAll(
            [](uint8_t slave_id, uint32_t objid, void* data, int s, int retry) {
                adapt_void_ptr(data, uint32_t{0x01020304});  // 假定获取到的返回值为特定值
                return 0;
            },
            Return(0)));

    EXPECT_EQ(board->sysio()->query_Tio_version(), 0);
    EXPECT_EQ(board->sysio()->get_tio_version(), 0x01020304);
    EXPECT_EQ(board->sysio()->hw_version_, 0x0102);
    EXPECT_EQ(board->sysio()->sw_version_, 0x0304);
}

// 测试tio_set_vout_enable
TEST_F(TioV3BoardTest, TioSetVoutEnable)
{
    EXPECT_CALL(*mockBus, set_sdo_blocking(_, TIO_VOUT_ENABLE_RW, _, _, _, _))
        .WillRepeatedly(DoAll(
            [](uint8_t slave_id, uint32_t objid, void* data, int s, void* respdata, int retry) {
                adapt_void_ptr(respdata, *(uint32_t*)data);  // 假定写入的值就是返回值
                return 0;
            },
            Return(0)));

    EXPECT_EQ(board->sysio()->tio_set_vout_enable(1), 0);
    EXPECT_EQ(board->sysio()->tio_get_vout_enable(), 1);
    EXPECT_EQ(board->sysio()->cmd_cfg_.cfg_vout_enable, 1);
    EXPECT_EQ(board->sysio()->fb_cfg_.cfg_vout_enable, 1);

    EXPECT_EQ(board->sysio()->tio_set_vout_enable(0), 0);
    EXPECT_EQ(board->sysio()->tio_get_vout_enable(), 0);
    EXPECT_EQ(board->sysio()->cmd_cfg_.cfg_vout_enable, 0);
    EXPECT_EQ(board->sysio()->fb_cfg_.cfg_vout_enable, 0);
}

// 测试tio_set_vout_enable
TEST_F(TioV3BoardTest, TioSetVoutVoltage)
{
    EXPECT_CALL(*mockBus, set_sdo_blocking(_, TIO_OUT_VOLTAGE_RW, _, _, _, _))
        .WillRepeatedly(DoAll(
            [](uint8_t slave_id, uint32_t objid, void* data, int s, void* respdata, int retry) {
                adapt_void_ptr(respdata, *(uint32_t*)data);  // 假定写入的值就是返回值
                return 0;
            },
            Return(0)));

    EXPECT_EQ(board->sysio()->tio_set_vout_voltage(1), 0);
    EXPECT_EQ(board->sysio()->tio_get_vout_voltage(), 1);
    EXPECT_EQ(board->sysio()->cmd_cfg_.cfg_vout_voltage, 1);
    EXPECT_EQ(board->sysio()->fb_cfg_.cfg_vout_voltage, 1);
    EXPECT_EQ(board->sysio()->tio_set_vout_voltage(0), 0);
    EXPECT_EQ(board->sysio()->tio_get_vout_voltage(), 0);
    EXPECT_EQ(board->sysio()->cmd_cfg_.cfg_vout_voltage, 0);
    EXPECT_EQ(board->sysio()->fb_cfg_.cfg_vout_voltage, 0);
}

// 测试tio_set_setup_di_pin_mode
TEST_F(TioV3BoardTest, TioSetupPinMode)
{
    EXPECT_CALL(*mockBus, set_sdo_blocking(_, _, _, _, _, _))
        .WillRepeatedly(DoAll(
            [](uint8_t slave_id, uint32_t objid, void* data, int s, void* respdata, int retry) {
                adapt_void_ptr(respdata, *(uint32_t*)data);  // 假定写入的值就是返回值
                return 0;
            },
            Return(0)));

    EXPECT_EQ(board->usrio()->tio_setup_di_pin_mode(0x11), 0);
    EXPECT_EQ(board->usrio()->tio_get_di_pin_mode(), 0x11);
    EXPECT_EQ(board->usrio()->cmd_.di_config, 0x11);
    EXPECT_EQ(board->usrio()->stat_.di_config, 0x11);
    EXPECT_EQ(board->usrio()->tio_setup_di_pin_mode(0x01), 0);
    EXPECT_EQ(board->usrio()->tio_get_di_pin_mode(), 0x01);
    EXPECT_EQ(board->usrio()->cmd_.di_config, 0x01);
    EXPECT_EQ(board->usrio()->stat_.di_config, 0x01);

    EXPECT_EQ(board->usrio()->tio_setup_do_pin_mode(0x11), 0);
    EXPECT_EQ(board->usrio()->tio_get_do_pin_mode(), 0x11);
    EXPECT_EQ(board->usrio()->cmd_.do_config, 0x11);
    EXPECT_EQ(board->usrio()->stat_.do_config, 0x11);
    EXPECT_EQ(board->usrio()->tio_setup_do_pin_mode(0xFF), 0);
    EXPECT_EQ(board->usrio()->tio_get_do_pin_mode(), 0xFF);
    EXPECT_EQ(board->usrio()->cmd_.do_config, 0xFF);
    EXPECT_EQ(board->usrio()->stat_.do_config, 0xFF);

    EXPECT_EQ(board->usrio()->tio_setup_ai_pin_mode(0x11), 0);
    EXPECT_EQ(board->usrio()->tio_get_ai_pin_mode(), 0x11);
    EXPECT_EQ(board->usrio()->cmd_.ai_config, 0x11);
    EXPECT_EQ(board->usrio()->stat_.ai_config, 0x11);
    EXPECT_EQ(board->usrio()->tio_setup_ai_pin_mode(0x0), 0);
    EXPECT_EQ(board->usrio()->tio_get_ai_pin_mode(), 0x0);
    EXPECT_EQ(board->usrio()->cmd_.ai_config, 0x0);
    EXPECT_EQ(board->usrio()->stat_.ai_config, 0x0);
}

// 测试接收pdo 0x190
TEST_F(TioV3BoardTest, TioGetPdo)
{
    int alive_rt = 0;
    EXPECT_CALL(*mockBus, get_rxpdo_data(_, _)).WillRepeatedly(Return(0));
    EXPECT_CALL(*mockBus, get_rxpdo_data(TIO_STATUS_R, _))
        .Times(1)
        .WillOnce(DoAll(
            [](uint32_t objid, uint8_t* data) {
                uint8_t cmd_data[8] = {0b100, 0b10, 1, 0, 255, 0, 0, 0b110000};  //构造0x190报文 薄膜按钮被按下,di[1]置1, do[0]置1, Ai[1]置1, rgb_yellow
                adapt_void_ptr(data, *(uint64_t*)cmd_data);
                return 0;
            },
            Return(alive_rt++)));  //模拟自增量假定写入的值就是返回值
    // std::vector<int8_t> input = {1, 0, 1, 1, 0, 0, 1, 0};  // 测试多余的输入会被舍弃
    // EXPECT_EQ(board->usrio()->set_do(input), 0);
    // EXPECT_EQ(board->usrio()->cmd_.set_do, 0b00000001);
    board->get_recv_data(1);  //mock模拟返回固定值，所以不需要set
    EXPECT_EQ(board->sysio()->fb_keys_.UIKey_point, 0);
    EXPECT_EQ(board->sysio()->fb_keys_.UIKey_custome, 0);
    EXPECT_EQ(board->sysio()->fb_keys_.UIKey_free, 0);
    EXPECT_EQ(board->sysio()->fb_keys_.UIKey_pause_resume, 1);
    EXPECT_EQ(board->usrio()->stat_.do_buffer, 0b01);
    EXPECT_EQ(board->usrio()->stat_.di_buffer, 0b10);
    EXPECT_EQ(board->usrio()->stat_.ai[0], 0);
    EXPECT_EQ(board->usrio()->stat_.ai[1], 255);
    EXPECT_EQ(board->sysio()->fb_RGB_, rtdev::TIO_LAMP_YELLOW);
    std::vector<int8_t> digital_output;
    std::vector<int8_t> check_do_array = {1, 0};
    EXPECT_EQ(board->usrio()->get_do(digital_output), 0);
    EXPECT_EQ(digital_output, check_do_array);
    std::vector<int8_t> digital_input;
    std::vector<int8_t> check_di_array = {0, 1};
    EXPECT_EQ(board->usrio()->get_di(digital_input), 0);
    EXPECT_EQ(digital_input, check_di_array);
    std::vector<double> Analog_input;
    std::vector<double> check_ai_array = {0.0, 255.0};
    EXPECT_EQ(board->usrio()->get_ai(Analog_input), 0);
    EXPECT_EQ(Analog_input, check_ai_array);
}

// 测试接收pdo 0x210
TEST_F(TioV3BoardTest, TioSetPdo)
{
    int alive_rt = 0;
    uint8_t cmd_data[8] = {0};
    EXPECT_CALL(*mockBus, set_txpdo_data(_, _)).WillRepeatedly(Return(0));
    EXPECT_CALL(*mockBus, set_txpdo_data(TIO_STATUS_W, _)).Times(1).WillOnce(Return(alive_rt++));
    std::vector<int8_t> input = {1, 0, 1, 1, 0, 0, 1, 0};  // 测试多余的输入会被舍弃
    EXPECT_EQ(board->usrio()->set_do(input), 0);
    EXPECT_EQ(board->usrio()->cmd_.set_do, 0b00000001);
    EXPECT_EQ(board->sysio()->set_tio_RGB(rtdev::TIO_LAMP_RED), 0);
    EXPECT_EQ(board->sysio()->cmd_RGB_, rtdev::TIO_LAMP_RED);
    board->set_send_data(1);  //mock模拟返回固定值，所以不需要set
}